<!--
    by: yuanbiao 2021年04月02日
    name: x-image-compare
    notes:
-->
<template>
  <div :class="prefixCls" ref="xImageCompare">
    <canvas :class="`${prefixCls}__test`" ref="cvsTest" :width="SCREEN_WIDTH" :height="SCREEN_HEIGHT"></canvas>
    <canvas :class="`${prefixCls}__testbin`" ref="cvsTestBin" :width="FRAME_WIDTH" :height="FRAME_HEIGHT"
      :style="{left: `${FRAME_X}px`, top: `${FRAME_Y}px`, right: `${FRAME_X + FRAME_WIDTH}px`, bottom: `${FRAME_Y + FRAME_HEIGHT}px`}"
    ></canvas>
    <canvas :class="`${prefixCls}__demo`" ref="cvsDemo" :width="FRAME_WIDTH" :height="FRAME_HEIGHT"
      :style="{left: `${FRAME_X}px`, top: `${FRAME_Y}px`, right: `${FRAME_X + FRAME_WIDTH}px`, bottom: `${FRAME_Y + FRAME_HEIGHT}px`}"
    ></canvas>
    <canvas :class="`${prefixCls}__result`" ref="cvsResult" :width="FRAME_WIDTH" :height="FRAME_HEIGHT"></canvas>
    <canvas :class="`${prefixCls}__screen`" ref="cvsScreen" :width="SCREEN_WIDTH" :height="SCREEN_HEIGHT"></canvas>
    <canvas :class="`${prefixCls}__demobin`" ref="cvsDemoBin" :width="FRAME_WIDTH" :height="FRAME_HEIGHT"></canvas>
    <canvas :class="`${prefixCls}__overlay`"
      ref="cvsOverlay"
      :width="SCREEN_WIDTH" :height="SCREEN_HEIGHT"
      @mousedown="mouseHandler"
      @mouseup="mouseHandler"
      @mouseleave="mouseHandler"
      @mouseenter="mouseHandler"
      @mousemove="mouseHandler"
      @touchstart.prevent="touchHandler"
      @touchmove.prevent="touchHandler"
      @touchend.prevent="touchHandler"
    ></canvas>
    <div v-if="optionsStore.toolsbarShow" :class="`${prefixCls}__toolsbar`">
      <div :class="`${prefixCls}__toolsbar_item`" v-for="(v, i) in toolsbarList" :key="i">
        <span :class="`${prefixCls}__toolsbar_btn`" :title="v.label" @click="toolsbarItemClick(v.name)">
          <i :class="v.icon"></i>
        </span>
      </div>
    </div>
  </div>
</template>

<script>
import {loadImage} from '@/script/utils.js'
const prefixCls = 'x-image-compare'
class VIEWPORT {
  constructor (x, y, w, h, rotate, scale, centerX, centerY) {
    this._x = this.x = x // 指示视口中心在原图的x坐标
    this._y = this.y = y // 指示视口中心在原图的y坐标
    this.w = w // 指示视口宽度
    this.h = h // 指示视口高度
    this.rotate = rotate // 指示原图的角度
    this.scale = scale // 指示原图的缩放比例（值越大，原图在视口中看起来就越大）
    this._cx = centerX
    this._cy = centerY
    this._drag_state = 0
  }
  // 指示该视口的中心在视口的x坐标，视口绕中心旋转和缩放
  get centerX () {
    return this._cx
  }
  // 指示该视口的中心在视口的y坐标，视口绕中心旋转和缩放
  get centerY () {
    return this._cy
  }
  // 以视口坐标平移
  translate (x, y) {
    this.x -= (Math.cos(this.rotate) * x + Math.sin(this.rotate) * y) / this.scale
    this.y -= (Math.cos(this.rotate) * y - Math.sin(this.rotate) * x) / this.scale
  }
  draw (fromImage, toCanvas, offsetX, offsetY) {
    offsetX = offsetX || 0
    offsetY = offsetY || 0
    let context = toCanvas.getContext('2d')
    context.setTransform(1, 0, 0, 1, 0, 0)
    // context.clearRect(offsetX, offsetY, toCanvas.width, toCanvas.height);
    context.translate(this._cx, this._cy)
    context.scale(this.scale, this.scale)
    context.rotate(this.rotate)
    context.translate(-this.x, -this.y)
    context.drawImage(fromImage, offsetX || 0, offsetY || 0)
    context.setTransform(1, 0, 0, 1, 0, 0)
  }
}
export default {
  name: 'x-image-compare',
  props: {
    options: {
      type: Object,
      default () {
        return {}
      }
    }
  },
  data() {
    return {
      prefixCls: prefixCls,
      inited: false,
      SCREEN_WIDTH: 0,
      SCREEN_HEIGHT: 0,
      FRAME_WIDTH: 0,
      FRAME_HEIGHT: 0,
      FRAME_X: 0,
      FRAME_Y: 0,
      CENTER_X: 0,
      CENTER_Y: 0,
      ROTATE_HOTSPOT_R: 0,
      optionsStore: {
        demoSrc: '',
        testSrc: '',
        demoInvertColors: false,
        thresholdAuto: false,
        resizeHandle: false,
        toolsbarShow: true
      },
      touchState: 0,
      touchesPrev: null,
      demoImgObj: new Image(),
      testImgObj: new Image(),
      testImgDirection: 0,
      viewportScreen: null,
      ctxScreen: null,
      ctxTestBin: null,
      ctxDemo: null,
      ctxTest: null,
      ctxOverlay: null,
      ctxResult: null,
      thresholdActive: 128,
      toolsbarList: [
        {name: 'invert', label: '范字反色', icon: 'xvu-iconfont xvu-loop'},
        {name: 'evalute', label: '评测', icon: 'xvu-iconfont xvu-menu'},
        {name: 'restore', label: '还原', icon: 'xvu-iconfont xvu-restore'}
      ]
    }
  },
  mounted () {
    this.$nextTick(() => {
      this.init()
      this.inited = true
    })
  },
  watch: {
    options: {
      handler (n, o) {
        console.log(n, o)
        this.mergeOptions(n)
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    mergeOptions (data) {
      Object.keys(data).forEach(k => {
        if (this.optionsStore[k] === data[k]) return
        this.$set(this.optionsStore, k, data[k])
        if (!this.inited) return
        switch (k) {
          case 'demoSrc':
            this.loadDemoImage()
            break
          case 'testSrc':
            this.loadTestImage()
            break
          case 'demoInvertColors':
            this.demoInvertColors()
            break
          case 'resizeHandle':
            this.resizeEvent()
            break
        }
      })
    },
    toolsbarItemClick (action) {
      if (action === 'restore') {
        this.resetData()
      } else if (action === 'invert') {
        this.optionsStore.demoInvertColors = !this.optionsStore.demoInvertColors
        this.demoInvertColors()
      } else if (action === 'evalute') {
        let score = this.getScore()
        let binPhotoSrc = this.getBinImgSrc()
        let binDemoSrc = this.getDemoBinImgSrc()
        if (isNaN(Number(score)) || !binPhotoSrc) {
          // 评测失败
          console.log('evalute result:评测失败', {binDemoSrc, binPhotoSrc, score})
        } else {
          this.$emit('evalute', {binDemoSrc, binPhotoSrc, score})
          console.log('evalute result:评测完成', {binDemoSrc, binPhotoSrc, score})
        }
      }
    },
    init () {
      if (!this.$refs.xImageCompare) return
      let xImageCompare = this.$refs.xImageCompare
      // 基本尺寸
      this.SCREEN_WIDTH = xImageCompare.clientWidth
      this.SCREEN_HEIGHT = xImageCompare.clientHeight
      this.FRAME_WIDTH = this.FRAME_HEIGHT = Math.min(this.SCREEN_WIDTH, this.SCREEN_HEIGHT)
      this.FRAME_X = (this.SCREEN_WIDTH - this.FRAME_WIDTH) / 2
      this.FRAME_Y = (this.SCREEN_HEIGHT - this.FRAME_HEIGHT) / 2
      this.CENTER_X = this.FRAME_X + this.FRAME_WIDTH / 2
      this.CENTER_Y = this.FRAME_Y + this.FRAME_HEIGHT / 2
      this.ROTATE_HOTSPOT_R = this.FRAME_HEIGHT * Math.SQRT1_2
      // DOM
      this.ctxScreen = this.$refs.cvsScreen ? this.$refs.cvsScreen.getContext('2d') : null
      this.ctxDemo = this.$refs.cvsDemo ? this.$refs.cvsDemo.getContext('2d') : null
      this.ctxTest = this.$refs.cvsTest ? this.$refs.cvsTest.getContext('2d') : null
      this.ctxOverlay = this.$refs.cvsOverlay ? this.$refs.cvsOverlay.getContext('2d') : null
      this.ctxDemoBin = this.$refs.cvsDemoBin ? this.$refs.cvsDemoBin.getContext('2d') : null
      this.ctxTestBin = this.$refs.cvsTestBin ? this.$refs.cvsTestBin.getContext('2d') : null
      this.ctxResult = this.$refs.cvsResult ? this.$refs.cvsResult.getContext('2d') : null
      // this.ctxScreen.setTransform(1, 0, 0, 1, 0, 0)
      // this.ctxScreen.strokeStyle = 'red'
      // this.ctxScreen.beginPath()
      // this.ctxScreen.arc(this.CENTER_X, this.CENTER_Y, this.ROTATE_HOTSPOT_R, 0, 2 * Math.PI)
      // this.ctxScreen.stroke()
      // this.ctxScreen.clearRect(0, 0, this.SCREEN_WIDTH, this.FRAME_Y + this.FRAME_HEIGHT)
      this.ctxScreen.clearRect(0, 0, this.SCREEN_WIDTH, this.SCREEN_HEIGHT)
      this.ctxScreen.strokeRect(this.FRAME_X, this.FRAME_Y, this.FRAME_WIDTH, this.FRAME_HEIGHT)
      // 加载图片
      this.loadDemoImage().then(() => {
        this.demoInvertColors()
      })
      this.loadTestImage().then(() => {
        let scale = this.FRAME_WIDTH / this.$refs.cvsTest.width
        this.viewportScreen = new VIEWPORT(this.$refs.cvsTest.width / 2, this.$refs.cvsTest.height / 2, this.SCREEN_WIDTH, this.SCREEN_HEIGHT, 0, scale, this.CENTER_X, this.CENTER_Y)
        this.updateCanvas()
      })
    },
    // 加载demo图片
    loadDemoImage () {
      this.ctxDemo = this.ctxDemo || (this.$refs.cvsDemo ? this.$refs.cvsDemo.getContext('2d') : null)
      if (!this.ctxDemo || !this.optionsStore.demoSrc) return Promise.reject('画板或图片无效')
      this.ctxDemo.clearRect(0, 0, this.FRAME_WIDTH, this.FRAME_HEIGHT)
      return loadImage(this.optionsStore.demoSrc || '').then((imgObj) => {
        this.ctxDemo.drawImage(imgObj, 0, 0, this.FRAME_WIDTH, this.FRAME_HEIGHT)
        this.demoImgObj = imgObj
      })
    },
    // 加载test图片
    loadTestImage () {
      this.ctxTest = this.ctxTest || (this.$refs.cvsTest ? this.$refs.cvsTest.getContext('2d') : null)
      if (!this.ctxTest || !this.optionsStore.testSrc) return Promise.reject('画板或图片无效')
      this.ctxTest.clearRect(0, 0, this.$refs.cvsTest.width, this.$refs.cvsTest.height)
      return loadImage(this.optionsStore.testSrc || '').then((imgObj) => {
        this.ctxTest.setTransform(1, 0, 0, 1, 0, 0)
        this.$refs.cvsTest.width = (this.testImgDirection + 1) % 2 * imgObj.width + this.testImgDirection % 2 * imgObj.height
        this.$refs.cvsTest.height = (this.testImgDirection + 1) % 2 * imgObj.height + this.testImgDirection % 2 * imgObj.width
        this.ctxTest.translate([0, imgObj.height, imgObj.width, 0][this.testImgDirection], [0, 0, imgObj.height, imgObj.width][this.testImgDirection])
        this.ctxTest.rotate(this.testImgDirection * Math.PI / 2)
        this.ctxTest.drawImage(imgObj, 0, 0, this.$refs.cvsTest.width, this.$refs.cvsTest.height)
        this.testImgObj = imgObj
      })
    },
    // resize事件绑定
    resizeEvent () {
      if (!this.optionsStore.resizeHandle) return
      let timer = null
      window.addEventListener('resize', () => {
        clearTimeout(timer)
        timer = setTimeout(() => { this.init() }, 300)
      })
    },
    // mouse事件处理（PC端）
    mouseHandler (event) {
      if (event.type === 'mouseleave') {
        this.touchState = 0
      } else if (event.type !== 'mousemove') {
        if (event.buttons !== 1) {
          this.touchState = 0
        } else {
          this.touchState = event.offsetY > this.FRAME_Y + this.FRAME_HEIGHT && Math.pow(this.CENTER_X - event.offsetX, 2) + Math.pow(this.CENTER_Y - event.offsetY, 2) < this.ROTATE_HOTSPOT_R * this.ROTATE_HOTSPOT_R ? 2 : 1
        }
      } else {
        if (this.touchState === 1) {
          this.viewportScreen.translate(event.movementX, event.movementY)
          this.updateCanvas()
        } else if (this.touchState === 2) {
          let temp = Math.atan2(event.offsetY - this.CENTER_Y, event.offsetX - this.CENTER_X) -
              Math.atan2(event.offsetY - event.movementY - this.CENTER_Y, event.offsetX - event.movementX - this.CENTER_X)
          this.viewportScreen.rotate += temp
          this.$emit('rotateChange', this.viewportScreen.rotate)
          this.updateCanvas()
        }
      }
    },
    // touch事件处理（移动端）
    touchHandler (event) {
      let touches = event.touches
      if (event.type === 'touchstart' || event.type === 'touchend') {
        if (touches.length === 1) {
          this.touchesPrev = {
            center: {
              x: touches[0].pageX,
              y: touches[0].pageY
            }
          }
        } else if (touches.length === 2) {
          this.touchesPrev = this.calcTouches(touches[0], touches[1])
        }
      } else if (event.type === 'touchmove') {
        if (touches.length === 1) {
          this.viewportScreen.translate(touches[0].pageX - this.touchesPrev.center.x, touches[0].pageY - this.touchesPrev.center.y)
          this.touchesPrev = {
            center: {
              x: touches[0].pageX,
              y: touches[0].pageY
            }
          }
          this.updateCanvas()
        } else if (touches.length === 2) {
          let temp = this.calcTouches(touches[0], touches[1])
          console.log('=======\r', temp, '\r', this.touchesPrev)
          this.viewportScreen.translate(temp.center.x - this.touchesPrev.center.x, temp.center.y - this.touchesPrev.center.y)
          this.viewportScreen.scale *= temp.distance / this.touchesPrev.distance
          this.$emit('scaleChange', this.viewportScreen.scale)
          this.viewportScreen.rotate += temp.degree - this.touchesPrev.degree
          this.$emit('rotateChange', this.viewportScreen.rotate)
          this.touchesPrev = temp
        }
      }
    },
    updateCanvas () {
      this.ctxScreen = this.ctxScreen || (this.$refs.cvsScreen ? this.$refs.cvsScreen.getContext('2d') : null)
      this.ctxScreen.clearRect(0, 0, this.$refs.cvsScreen.width, this.$refs.cvsScreen.height)
      this.viewportScreen.draw(this.$refs.cvsTest, this.$refs.cvsScreen, 0, 0)
      this.binaryzationHandler()
    },
    demoInvertColors () {
      if (!this.ctxDemoBin) this.ctxDemoBin = this.$refs.cvsDemoBin ? this.$refs.cvsDemoBin.getContext('2d') : null
      this.ctxDemoBin.clearRect(0, 0, this.$refs.cvsDemoBin.width, this.$refs.cvsDemoBin.height)
      let imgDataDemo = this.ctxDemo.getImageData(0, 0, this.$refs.cvsDemo.width, this.$refs.cvsDemo.height)
      let binImgDataDemo = new ImageData(this.binaryzationCalc(imgDataDemo.data, this.optionsStore.demoInvertColors), imgDataDemo.width, imgDataDemo.height)
      this.ctxDemoBin.putImageData(binImgDataDemo, 0, 0)
      return this.$refs.cvsDemoBin.toDataURL('image/png')
    },
    getDemoBinImgSrc () {
      return this.$refs.cvsDemoBin.toDataURL('image/png')
    },
    binaryzationHandler () {
      const imgData = this.ctxScreen.getImageData(this.FRAME_X, this.FRAME_Y, this.FRAME_WIDTH, this.FRAME_HEIGHT)
      const binImgData = new ImageData(this.binaryzationCalc(imgData.data), imgData.width, imgData.height)
      this.ctxScreen.putImageData(binImgData, this.FRAME_X, this.FRAME_Y)
    },
    // 数据二值化计算
    binaryzationCalc (data, flag) {
      // 是否自动计算阈值
      let devide = this.optionsStore.thresholdAuto ? this.getThresholdByOtsu(data) : this.thresholdActive
      if (this.optionsStore.thresholdAuto && this.thresholdActive !== devide) {
        this.thresholdActive = devide
        this.$emit('thresholdChange', devide)
      }
      if (flag) {
        for (let i = data.length - 4 + 0; i > -1; i -= 4) {
          let gray = this.calcGray(data[i], data[i + 1], data[i + 2])
          let byte = (data[i + 3] === 0 || gray >= devide) ? 0 : 255
          data[i + 0] = byte
          data[i + 1] = byte
          data[i + 2] = byte
          data[i + 3] = 255
        }
      } else {
        for (let i = data.length - 4 + 0; i > -1; i -= 4) {
          let gray = this.calcGray(data[i], data[i + 1], data[i + 2])
          let byte = data[i + 3] === 0 || gray >= devide ? 255 : 0
          data[i + 0] = byte
          data[i + 1] = byte
          data[i + 2] = byte
          data[i + 3] = 255
        }
      }
      return data
    },
    // 重叠对比二值化图片
    binaryzationCompare ({testCanvas, demoCanvas, cvsResult, offsetX, offsetY}) {
      const getData = (canvas, offsetX, offsetY, width, height) => {
        offsetX = offsetX || 0
        offsetY = offsetY || 0
        width = width || canvas.width
        height = height || canvas.height
        return canvas.getContext('2d').getImageData(offsetX, offsetY, width, height).data
      }
      let data1 = getData(testCanvas, offsetX, offsetY, demoCanvas.width, demoCanvas.height)
      let data2 = getData(demoCanvas)
      let dataResult = new Uint8ClampedArray(data2.length)
      let black = 0
      let white = 0
      const COLOR_MAP = {
        0: { 0: [14, 14, 14, 255], 255: [14, 14, 14, 255] },
        255: { 0: [252, 218, 215, 255], 255: [252, 253, 247, 255] }
      }
      for (let i = data1.length - 4 + 0; i > -1; i -= 4) {
        let l = Math.floor(data1[i] / 128) * 255
        let r = Math.floor(data2[i] / 128) * 255
        const color = COLOR_MAP[l][r]
        dataResult[i] = color[0]
        dataResult[i + 1] = color[1]
        dataResult[i + 2] = color[2]
        dataResult[i + 3] = color[3]
        black += l === 0 && r === 0
        white += l !== 0 && r !== 0
      }
      let temp = new ImageData(dataResult, cvsResult.width, cvsResult.height)
      cvsResult.getContext('2d').putImageData(temp, 0, 0)
      return (black + white) / (data1.length / 4)
    },
    getAlignmentScore (testCanvas, demoCanvas, cvsResult, offset) { // 获取重叠对比分数
      const getImgData = (canvas) => canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height)
      const imgData1 = getImgData(testCanvas)
      const imgData2 = getImgData(demoCanvas)
      let maxv = [0]; let maxx = 0; let maxy = 0
      console.time('== SEARCHING ==', offset)
      // for (let i = 0; i <= offset * 2; i += 4) {
      //   for (let j = 0; j <= offset * 2; j += 4) {
      //     let temp = this.calcOverlapRate(imgData1, imgData2, i, j, demoCanvas)
      //     if (temp[0] > maxv[0]) {
      //       maxx = i
      //       maxy = j
      //       maxv = temp
      //     }
      //   }
      // }
      maxv = this.calcOverlapRate(imgData1, imgData2, 0, 0, demoCanvas)
      console.timeEnd('== SEARCHING ==')
      console.log(maxx, maxy, maxv)
      console.log(testCanvas, demoCanvas, cvsResult)

      // this.binaryzationCompare(testCanvas, demoCanvas, cvsResult, maxx, maxy)
      // let count = (maxv[0] / demoCanvas.width / demoCanvas.width) || 0
      // let count1 = (maxv[1] / maxv[2]) || 0
      // let count2 = (maxv[1] * 2) / (maxv[1] + maxv[2]) || 0
      // let count3 = (maxv[3] / demoCanvas.width / demoCanvas.width) || 0
      // console.log(`得分1 ${count}\n得分2 ${count1}\n得分3 ${count2}\n得分4 ${count3}\n`)
      // return {count, count1, count2, count3}
      let score1 = (maxv[1] * 2) / (maxv[1] + maxv[3]) * 100 || 0
      let score2 = maxv[0] / (maxv[2] + maxv[3]) * 100 || 0
      let score = 0
      if (score1 && score2) {
        score = Math.min((score1 + score2) / 2, 100)
        score = parseFloat(score.toFixed(1))
      }
      if (maxv[4] === 0 || maxv[5] === 0) {
        score = score1
      }
      console.log(`得分1 ${score1}\n得分2 ${score2}\n平均分${score}`)
      return score
    },
    getScore () {
      // 评分时，为了保证图片为正方形，将其单独渲染在一个canvas上
      if (!this.ctxTestBin) this.ctxTestBin = this.$refs.cvsTestBin ? this.$refs.cvsTestBin.getContext('2d') : null
      this.ctxTestBin.clearRect(0, 0, this.$refs.cvsTestBin.width, this.$refs.cvsTestBin.height)
      let imgData = this.ctxScreen.getImageData(this.FRAME_X, this.FRAME_Y, this.FRAME_WIDTH, this.FRAME_HEIGHT)
      let binImgData = new ImageData(this.binaryzationCalc(imgData.data), imgData.width, imgData.height)
      this.ctxTestBin.putImageData(binImgData, 0, 0)
      // 评分时，为了提高相似度判断精度，将demo图片进行二值化
      // 一次评测，假设demo为白底黑字
      if (!this.ctxDemoBin) this.ctxDemoBin = this.$refs.cvsDemoBin ? this.$refs.cvsDemoBin.getContext('2d') : null
      this.ctxDemoBin.clearRect(0, 0, this.$refs.cvsDemoBin.width, this.$refs.cvsDemoBin.height)
      let imgDataDemo = this.ctxDemo.getImageData(0, 0, this.$refs.cvsDemo.width, this.$refs.cvsDemo.height)
      let binImgDataDemo = new ImageData(this.binaryzationCalc(imgDataDemo.data, this.optionsStore.demoInvertColors), imgDataDemo.width, imgDataDemo.height)
      this.ctxDemoBin.putImageData(binImgDataDemo, 0, 0)
      // 开始一次评测
      // let scoreObject = this.getAlignmentScore(this.$refs.cvsTestBin, this.$refs.cvsDemoBin, this.$refs.cvsResult, 20)
      // let score = (scoreObject.count + scoreObject.count1) / 2
      // if (score > 0.65 && score <= 0.80) {
      //   score += 0.10
      // } else if (score > 0.80 && score <= 0.95) {
      //   score += 0.05
      // }
      // score = Math.min(Math.ceil(score * 100), 100)
      let score = this.getAlignmentScore(this.$refs.cvsTestBin, this.$refs.cvsDemoBin, this.$refs.cvsResult, 20)
      return score
    },
    getBinImgSrc () {
      if (!this.ctxTestBin) this.ctxTestBin = this.$refs.cvsTestBin ? this.$refs.cvsTestBin.getContext('2d') : null
      this.ctxTestBin.clearRect(0, 0, this.$refs.cvsTestBin.width, this.$refs.cvsTestBin.height)
      const imgData = this.ctxScreen.getImageData(this.FRAME_X, this.FRAME_Y, this.FRAME_WIDTH, this.FRAME_HEIGHT)
      const binImgData = new ImageData(this.binaryzationCalc(imgData.data), imgData.width, imgData.height)
      this.ctxTestBin.putImageData(binImgData, 0, 0)
      return this.$refs.cvsTestBin.toDataURL('image/png')
    },
    // 使用otsu方法获得阈值
    getThresholdByOtsu (data) {
      let pointsCount = 0 // 点的总数。因为部分点是透明的，不计入图像总面积。
      let graySum = 0 // 所有点的gray值之和
      let histogram = new Uint32Array(256) // 灰度直方图，每个灰度值对应的点的数量。
      for (let i = data.length - 4; i >= 0; i -= 4) {
        let gray = this.calcGray(data[i], data[i + 1], data[i + 2])
        if ( data[i + 3] === 0) continue
        pointsCount++
        graySum += gray
        histogram[gray]++
      }
      // 两种像素分别记为0，1.
      // 点数占图像比例分别为w0、w1
      // 平均灰度分别为u0、u1，总平均灰度为u
      // 具体算法见 https://blog.csdn.net/u011285477/article/details/52004513
      let w0 = 0
      let u0 = 0
      let maxg = -999999
      let maxi = 127
      for (let i = 0; i < 256; i++) {
        w0 += histogram[i]
        u0 += i * histogram[i]
        let g = w0 / (pointsCount - w0) * Math.pow(u0 / w0 - graySum / pointsCount, 2)
        if (g > maxg) {
          maxi = i
          maxg = g
        }
      }
      return maxi
    },
    // 计算图片1加上偏移后相对图片2的重合率
    calcOverlapRate  (imgData1, imgData2, offsetX, offsetY, canvas) {
      const imgw1 = imgData1.width
      const imgw2 = imgData2.width
      const x1 = Math.max(0, offsetX)
      const y1 = Math.max(0, offsetY)
      const x2 = Math.max(0, -offsetX)
      const y2 = Math.max(0, -offsetY)
      const data1 = imgData1.data
      const data2 = imgData2.data
      let count0 = 0
      let count1 = 0
      let count2 = 0
      let count3 = 0
      let count4 = 0
      let count5 = 0
      for (let i = 0; i < canvas.width; i++) {
        for (let j = 0; j < canvas.width; j++) {
          const c1 = data1[4 * (i + x1 + (j + y1) * imgw1)]
          const c2 = data2[4 * (i + x2 + (j + y2) * imgw2)]
          // count0 += (c1 === c2)             // 交集(黑色+白色)
          // count1 += (c1 === 0 && c2 === 0)  // 交集(黑色)
          // count2 += (c1 === 0 || c2 === 0)  // 并集(黑色+红色+蓝色)
          // count3 += (c2 === 0)              // 范字（黑色+红色）
          count0 += (c1 === c2)             // 交集(黑色+白色)
          count1 += (c1 === 0 && c2 === 0)  // 交集(黑色)
          count2 += (c1 > 0 && c2 > 0)      // 交集(白色)
          count3 += (c1 === 0 || c2 === 0)  // 并集(黑色+红色+蓝色)
          count4 += (c1 > 0 && c2 === 0)    // 并集(红色)
          count5 += (c1 === 0 && c2 > 0)    // 并集(蓝色)
        }
      }
      console.log(offsetX, offsetY, '====', count0, count1, count2, count3, count4, count5)
      return [count0, count1, count2, count3, count4, count5]
    },
    // 处理touches
    calcTouches (t1, t2) {
      return {
        center: { x: (t1.pageX + t2.pageX) / 2, y: (t1.pageY + t2.pageY) / 2 },
        distance: Math.sqrt(Math.pow(t1.pageX - t2.pageX, 2), Math.pow(t1.pageY - t2.pageY, 2)),
        degree: Math.atan2(t1.pageY - t2.pageY, t1.pageX - t2.pageX)
      }
    },
    // 根据RGB计算灰度值
    calcGray (R, G, B) {
      return Math.floor(R * 0.299 + G * 0.587 + B * 0.114)
    },
    // 修改rotate值（暴露方法，外用）
    setRotate (rotate) {
      this.viewportScreen.rotate = rotate
      this.updateCanvas()
    },
    // 修改scale值（暴露方法，外用）
    setScale (scale) {
      let _scale = Math.min(this.SCREEN_WIDTH, this.SCREEN_HEIGHT) / Math.max(this.$refs.cvsTest.width, this.$refs.cvsTest.height)
      this.viewportScreen.scale = scale * _scale
      this.updateCanvas()
    },
    // 修改threshold值（暴露方法，外用）
    setThreshold (threshold) {
      this.thresholdActive = threshold || 0
      this.updateCanvas()
    },
    // 重置数据
    resetData () {
      this.testImgDirection = 0
      this.touchState = 0
      if (this.thresholdActive !== 128) {
        this.thresholdActive = 128
        this.$emit('thresholdChange', this.thresholdActive)
      }
      this.viewportScreen.x = this.viewportScreen._x
      this.viewportScreen.y = this.viewportScreen._y
      if (this.viewportScreen.rotate !== 0) {
        this.viewportScreen.rotate = 0
        this.$emit('rotateChange', this.viewportScreen.rotate)
      }
      this.viewportScreen.scale = this.FRAME_WIDTH / this.$refs.cvsTest.width
      this.ctxTestBin.clearRect(0, 0, this.$refs.cvsTestBin.width, this.$refs.cvsTestBin.height)
      this.ctxResult.clearRect(0, 0, this.$refs.cvsResult.width, this.$refs.cvsResult.height)
      this.updateCanvas()
    }
  }
}
</script>
